﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using SpeakupBackend.Classes;
using WebLayer;
using System.Windows.Documents;
using System.Windows.Media;
using SpeakupBackend;
using System.Threading;
using System.Net;

namespace Frontend
{
    enum ActionGridHeader { ClassTime, ClassName, ClassStatus, ClassAction, OperationStatus, OperationStatusDescription }

    class ActionGrid: BorderedGrid
    {
        class SuperviserExceptionThrownEventArgs: EventArgs
        {
            public SuperviserExceptionThrownEventArgs(Exception ex)
            {
                m_exception = ex;
            }

            Exception m_exception;
            public Exception Exception
            {
                get
                {
                    return m_exception;
                }
            }
        }

        public event EventHandler<EventArgs> SuperviserDone;

        protected void OnSuperviserDone()
        {
            EventHandler<EventArgs> handler = SuperviserDone;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }

        event EventHandler<SuperviserExceptionThrownEventArgs> SuperviserExceptionThrown;

        protected void OnSuperviserExceptionThrown(Exception ex)
        {
            EventHandler<SuperviserExceptionThrownEventArgs>  handler = SuperviserExceptionThrown;
            if(handler != null)
            {
                handler(this, new SuperviserExceptionThrownEventArgs(ex));
            }
        }
        
        DelegateProxy<EventHandler<SuperviserExceptionThrownEventArgs>> SuperviserExceptionThrownProxy =
            new DelegateProxy<EventHandler<SuperviserExceptionThrownEventArgs>>();

        DelegateProxy<EventHandler<ClassRequestStartedEventArgs>> ClassRequestStartedProxy =
           new DelegateProxy<EventHandler<ClassRequestStartedEventArgs>>();

        DelegateProxy<EventHandler<ClassRequestResultEventArgs>> ClassRequestResultProxy = 
            new DelegateProxy<EventHandler<ClassRequestResultEventArgs>>();
                                   
        DelegateProxy<EventHandler<ClassVerificationStartedEventArgs>> ClassVerificationStartedProxy =
            new DelegateProxy<EventHandler<ClassVerificationStartedEventArgs>>();

        DelegateProxy<EventHandler<ClassVerificationResultEventArgs>> ClassVerificationResultProxy =
            new DelegateProxy<EventHandler<ClassVerificationResultEventArgs>>();
              

        enum ClassActionState { Pending, Started, Succeeded, Failed, Skipped }
        Dictionary<ClassActionState, Brush> ClassActionStateBrushes =
            new Dictionary<ClassActionState, Brush>
            {
                {ClassActionState.Pending, Brushes.LightGray},
                {ClassActionState.Started, Brushes.Yellow},
                {ClassActionState.Succeeded, Brushes.LightGreen},
                {ClassActionState.Failed, Brushes.Red},
                {ClassActionState.Skipped, Brushes.Orange},
            };

        public ActionGrid()
        {
            SuperviserExceptionThrown += SuperviserExceptionThrownProxy.ProxyConsumer;
            SuperviserExceptionThrownProxy.ProxySource += OnSuperviserExceptionThrown;

            Superviser.RequestStarted += ClassRequestStartedProxy.ProxyConsumer;
            ClassRequestStartedProxy.ProxySource += OnClassRequestStarted;

            Superviser.RequestResult += ClassRequestResultProxy.ProxyConsumer;
            ClassRequestResultProxy.ProxySource += OnClassRequestResult;

            Superviser.VerificationStarted += ClassVerificationStartedProxy.ProxyConsumer;
            ClassVerificationStartedProxy.ProxySource += OnClassVerificationStarted;

            Superviser.VerificationResult += ClassVerificationResultProxy.ProxyConsumer;
            ClassVerificationResultProxy.ProxySource += OnClassVerificationResult;

            SetupGrid();
        }

        void SetupGrid()
        {
            Children.Clear();
            RowDefinitions.Clear();
            ColumnDefinitions.Clear();

            SetupHeaders();
        }
        void AddColumn(ActionGridHeader headerId, string headerName, GridLength width)
        {
            if ((int)headerId != ColumnDefinitions.Count)
            {
                throw new InvalidProgramException("Bad header.");
            }
            
            ColumnDefinition coldef = new ColumnDefinition();
            coldef.Width = width; //new GridLength(1, GridUnitType.Star);
            ColumnDefinitions.Add(coldef);

            TextBlock txt = new TextBlock();
            txt.VerticalAlignment = VerticalAlignment.Center;
            txt.TextAlignment = TextAlignment.Center;
            txt.TextWrapping = TextWrapping.Wrap;
            txt.Inlines.Add(headerName);
            txt.Margin = new Thickness(10);
            txt.FontSize = 12;
            txt.FontWeight = FontWeights.DemiBold;

            Grid.SetRow(txt, 0);
            Grid.SetColumn(txt, ColumnDefinitions.Count - 1);
            Children.Add(txt);
        }
        void SetupHeaders()
        {
            RowDefinition rowdef = new RowDefinition();
            rowdef.Height = GridLength.Auto;
            RowDefinitions.Add(rowdef);

            AddColumn(ActionGridHeader.ClassTime, "Class Time", GridLength.Auto);
            AddColumn(ActionGridHeader.ClassName, "Class Name", GridLength.Auto);
            AddColumn(ActionGridHeader.ClassStatus, "Class Status", GridLength.Auto);
            AddColumn(ActionGridHeader.ClassAction, "Class Action", GridLength.Auto);
            AddColumn(ActionGridHeader.OperationStatus, "Operation Status", GridLength.Auto);
            AddColumn(ActionGridHeader.OperationStatusDescription, 
                        "Operation Status Description", 
                        new GridLength(1,GridUnitType.Star));
        }

        ClassActionSupervisor Superviser = new ClassActionSupervisor();
        Dictionary<Class, ActionGridEntry> Entries = new Dictionary<Class, ActionGridEntry>();
        Dictionary<Class, String> ServerReplies = new Dictionary<Class, String>();
        Dictionary<Class, bool> VerificationReplies = new Dictionary<Class, bool>();

        void ClearLastResults()
        {
            foreach (ActionGridEntry entry in Entries.Values)
            {
                entry.RemoveFromGrid(this);
            }
            Entries.Clear();

            Superviser.Clear();
            ServerReplies.Clear();
            VerificationReplies.Clear();
        }

        Thread SuperviserThread; 

        public void CancelActions()
        {
            Thread thread = SuperviserThread;
            if(thread != null)
            {
                if(thread.IsAlive)
                {
                    thread.Abort();
                }
            }            
        }

        public void ApplyActions(IEnumerable<Class> classes, LoginToken token)
        {
            ClearLastResults();

            List<Class> actionableClasses = (from n in classes
                                            where n.IsActionPending == true
                                            select n).ToList();

            foreach (Class cl in actionableClasses)
            {
                ActionGridEntry entry = new ActionGridEntry(cl);
                entry.SetOperationStatus("Pending", "Class is queued for processing....", Brushes.LightGray);

                RowDefinition rowdef = new RowDefinition();
                rowdef.Height = new GridLength(1, GridUnitType.Star);
                RowDefinitions.Add(rowdef);

                entry.AddToGrid(this, RowDefinitions.Count - 1);
                Entries.Add(cl, entry);
            }  

            List<Class> applicableClases = new List<Class>();
            foreach (Class cl in actionableClasses)
            {
                string message;
                if (!cl.IsActionApplicable(out message))
                {
                    Entries[cl].SetOperationStatus("Skipped", "Can not send request to server. Reason: " + message, 
                                                    Brushes.LightYellow);
                }
                else
                {
                    applicableClases.Add(cl);
                }
            }

            foreach (Class cl in actionableClasses)
            {
                Superviser.Add(cl);
            }

            SuperviserThread = new Thread(SupervisorThread);
            SuperviserThread.IsBackground = true;
            SuperviserThread.Start(token);
        }

        void OnClassRequestStarted(object sender, ClassRequestStartedEventArgs args)
        {
            if (Entries.Keys.Contains(args.Class))
            {
                Entries[args.Class].SetOperationStatus("Processing",
                    "Sending action to server...",
                    ClassActionStateBrushes[ClassActionState.Started]);
            }

            InvalidateVisual();
        }

        void OnClassRequestResult(object sender, ClassRequestResultEventArgs args)
        {
            if (Entries.Keys.Contains(args.Class))
            {
                ServerReplies[args.Class] = args.Message;
                Entries[args.Class].SetOperationStatus("Action Succeeded",
                    "Server reply was: \n" + args.Message,
                    ClassActionStateBrushes[ClassActionState.Succeeded]);
            }

            InvalidateVisual();
        }

        void OnClassVerificationStarted(object sender, ClassVerificationStartedEventArgs args)
        {
            if (Entries.Keys.Contains(args.Class))
            {
                Entries[args.Class].SetOperationStatus("Verifying",
                        "Verifying that requested action is actually reflected and saved on server", 
                        ClassActionStateBrushes[ClassActionState.Started]);
            }

            InvalidateVisual();
        }

        void OnClassVerificationResult(object sender, ClassVerificationResultEventArgs args)
        {
            if (Entries.Keys.Contains(args.Class))
            {
                if (args.Verified)
                {
                    Entries[args.Class].SetOperationStatus("Action Verified",
                        "Server reply was:\n" + ServerReplies[args.Class], 
                        ClassActionStateBrushes[ClassActionState.Succeeded]);
                }
                else
                {
                    Entries[args.Class].SetOperationStatus("Verification failure.",
                        "Requested action was sent to server, but server refused to sutisfy it. " +
                        "Server reply was:\n" + ServerReplies[args.Class],
                        ClassActionStateBrushes[ClassActionState.Failed]);
                }

                VerificationReplies.Add(args.Class, args.Verified);
            }

            InvalidateVisual();
        }        

        void OnSuperviserExceptionThrown(object sender, SuperviserExceptionThrownEventArgs args)
        {
            foreach (var entry in Entries.Values)
            {
                entry.SetOperationStatus("Exception thrown", args.Exception.Message, Brushes.Red);
            }

            InvalidateVisual();
        }

        public void SupervisorThread(Object arg)
        {
            LoginToken token = (LoginToken)arg;

            try
            {
                Superviser.ApplyActions(token);
            }
            catch (Exception ex)
            {
                OnSuperviserExceptionThrown(ex);
            }
            finally
            {
                OnSuperviserDone();
            }
        }
            
    }
}
